package com.macro.mall.common.filter;


import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.macro.mall.common.exception.ApiSignatureException;
import com.macro.mall.common.util.AESUtil;
import com.macro.mall.common.util.RSAUtil;
import com.macro.mall.common.util.RequestWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;


/**
 * @ClassName :  DecryptReplaceStreamFilter
 * @Description :Filter过滤器  解密请求参数。同时替换请求体，使后续操作无感知加密！！
 * @Author :  AD
 */

@Slf4j
public class DecryptReplaceStreamFilter implements Filter {

    /*
     * AKS（Authentication Key Management System），采用无明文密钥的方式对数据进行加密保护，加解密运算由统一的安全计算中心完成，AKS系统以接口的方式为对各个业务线提供加密解服务。
     *  各个系统不再使用明文密钥，只使用密钥别名，调用简单的加解密函数，完成对数据的保护。
     *  */
    //请求头标签开关：是否需要加密解密
    private static final String AKS_ENABLE = "aksEncrypt";
    //GET请求加密数据Key
    public static final String AKS_PARAMETER = "encryptData";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_POST = "POST";

    //POST请求加密数据Key
    private static final String AKS_BODY = "content";
    //AES密钥Key
    private static final String AES_KEY = "aesKey";

    /**
     * Feign是声明式Web Service客户端，它让微服务之间的调用变得更简单，类似controller调用service。
     * Feign内部调用时，请求头中标注请求源
     */
    public static final String SOURCE_KEY = "api-source";
    public static final String SOURCE_VALUE = "inner-api";

    @Value("${Rsa.PrivateKey}")
    private String privateKey;

    @Value("${API.Security.enable}")
    private boolean securityEnable;
    @Resource
    @Qualifier("handlerExceptionResolver")
    HandlerExceptionResolver resolver;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       if (!(servletRequest instanceof HttpServletRequestWrapper)) {//上传文件
           filterChain.doFilter(servletRequest, servletResponse);
           return;
       }

        //转换为自己Wrapper,实现多次读写
        RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);

        String contentType = requestWrapper.getContentType();    //请求头中获取Content-Type数据
        String requestURI = requestWrapper.getRequestURI();
        //配置文件配置是否开启加解密功能
        if (!securityEnable) {
            log.info("未开启接口安全加密传输! 无需解密请求参数！");
            filterChain.doFilter(requestWrapper, servletResponse);
            return;
        }
        //通过请求头参数判断该请求是否需要解密处理
        if (!needAks(requestWrapper)) {
            log.info("请求：{}，非加密请求，无需解密操作！", requestURI);

                filterChain.doFilter(requestWrapper, servletResponse);

            return;
        }
        /*
         *  [该功能暂时不用管，因为在请求头中不添加 aksEncrypt：true    键值对，请求接口时同样不会去进行解密请求数据的操作]
         *  */
        //Feign服务端调用内部请求，按照不加密的逻辑放行[Feign是声明式Web Service客户端，它让微服务之间的调用变得更简单，类似controller调用service。]
        //前端  -->  A  -->  b  -->  c
        String sourceKey = requestWrapper.getHeader(SOURCE_KEY);
        if (!ObjectUtils.isEmpty(sourceKey) && sourceKey.equals(SOURCE_VALUE)) {
            log.info("内部请求，无效加密解密接口数据！");
            filterChain.doFilter(requestWrapper, servletResponse);
            return;
        }

        /*
         *  POST请求进行解密工作
         *  */
        if (requestWrapper.getMethod().equalsIgnoreCase(METHOD_POST)) {

            //Get请求中  获取到指定加密的参数  然后进行解密操作
            String encryptData = requestWrapper.getParameter(AKS_PARAMETER);
            log.info("Post请求请求:{}，待解密请求参数：{}", requestWrapper.getRequestURI(), encryptData);
            //  先解密  存放在请求头中且经过RSA加密过的AES密钥
            String aesKey1 = requestWrapper.getParameter(AES_KEY);
            if (encryptData != null && !encryptData.isEmpty() && aesKey1 != null && !aesKey1.isEmpty()) {
                try {
                    byte[] aesKeyByte = RSAUtil.decryptByPrivateKey(RSAUtil.decryptBASE64(aesKey1), privateKey, RSAUtil.MAX_DECRYPT_BLOCK);
                    aesKey1 = new String(aesKeyByte, "UTF-8");
                    System.out.println("aesKey.toString()  =  " + aesKey1.toString());
                } catch (Exception e) {
                    //throw new RuntimeException(e);
                    //throw  new ApiSignatureException("签名错误");
                    resolver.resolveException((HttpServletRequest) requestWrapper, (HttpServletResponse) servletResponse, null, new ApiSignatureException("签名错误！"));
                    return;

                }
                //  解密数据操作  AKS_PARAMETER
                String decryptData = AESUtil.decryptFromBase64(encryptData, aesKey1);
                log.info("Get请求:{}，解密之后参数：{}", requestURI, decryptData);
                //将GET请求中的Parameter参数赋值入RequestWrapper中封装的其它参数存放集合Map中
                com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();

                Map<String, Object> map = new HashMap<>();
                if (!StringUtils.isBlank(decryptData)) {
                    map = objectMapper.readValue(decryptData, Map.class);
                    Set set = map.keySet();
                    for (Iterator iterator = set.iterator(); iterator.hasNext(); ) {
                        Object obj = (Object) iterator.next();
                        Object value = (Object) map.get(obj);
                        if (value == null || value.equals("") || value.equals("null") || obj.toString().length() == 0) {
                            iterator.remove();
                        }
                    }
                }
                //将aseKey数据也存入map额外参数中
                map.put("aesKey", aesKey1);
                requestWrapper.setParameterMap(map);
            }


            //读取JSON请求体数据
            StringBuffer bodyInfo = new StringBuffer();
            String line = null;
            BufferedReader reader = null;
            reader = requestWrapper.getReader();
            while ((line = reader.readLine()) != null) {
                bodyInfo.append(line);
            }
            //解密请求体数据
            JSONObject jsonObject = JSONUtil.parseObj(bodyInfo.toString());
            log.info("Post请求:{}，待解密请求参数：{}", requestURI, JSONUtil.toJsonStr(jsonObject));
            //获取通过AES加密之后的密文
            String content = jsonObject.getStr(AKS_BODY);
            //获取通过RSA加密之后的AES密钥KEy
            String aesKey = jsonObject.getStr(AES_KEY);
            //RSAUtil解密出AES密钥Key
            try {
                aesKey = new String(RSAUtil.decryptByPrivateKey(RSAUtil.decryptBASE64(aesKey), privateKey, RSAUtil.MAX_DECRYPT_BLOCK), "UTF-8");
            } catch (Exception e) {
                //  throw new RuntimeException(e);
                //throw  new ApiSignatureException("签名错误");
                resolver.resolveException((HttpServletRequest) requestWrapper, (HttpServletResponse) servletResponse, null, new ApiSignatureException("签名错误！"));
                return;
            }
            /*
             *  方式1.将解密之后的数据+aesKey [放入body中，弃：会影响body结构]  (  满足在AOP操作中对出参数据进行加密  )  一并交给下游业务。
             */
            //JSONObject  requestBody  =  JSONObject.parseObject(data);
            //requestBody.put(AES_KEY,aesKey);
            /*
             *  方式2.将其放入[请求对象属性中中]，不影响请求体结构！
             */
            //requestWrapper.setAttribute(AES_KEY,aesKey);
            /*
             *  方式3.将其放入[放入RequestWrapper封装的额外参数Map中]，不影响请求体结构！
             *  */
            HashMap<String, Object> map = new HashMap<>();
            map.put(AES_KEY, aesKey);
            requestWrapper.setParameterMap(map);

            //AESUtil  +  aesKey  解密json数据
            String decryptData = AESUtil.decryptFromBase64(content, aesKey);
            log.info("Post请求:{}，解密之后参数：{}", requestURI, decryptData);

            //重置Json请求体，保证下游业务无感知获取数据
            //requestWrapper.setBody(requestBody.toJSONString().getBytes());
            requestWrapper.setBody(decryptData.getBytes());
        }
        /*
         * GET请求解密处理：[AES密钥Key放置在请求头中]
         *  */
        else if (requestWrapper.getMethod().equalsIgnoreCase(METHOD_GET)) {
            //Get请求中  获取到指定加密的参数  然后进行解密操作
            String encryptData = requestWrapper.getParameter(AKS_PARAMETER);
            log.info("Get请求:{}，待解密请求参数：{}", requestWrapper.getRequestURI(), encryptData);
            //  先解密  存放在请求头中且经过RSA加密过的AES密钥
            String aesKey = requestWrapper.getParameter(AES_KEY);
            if (encryptData != null && !encryptData.isEmpty() && aesKey != null && !aesKey.isEmpty()) {
                try {
                    byte[] aesKeyByte = RSAUtil.decryptByPrivateKey(RSAUtil.decryptBASE64(aesKey), privateKey, RSAUtil.MAX_DECRYPT_BLOCK);
                    aesKey = new String(aesKeyByte, "UTF-8");
                    System.out.println("aesKey.toString()  =  " + aesKey.toString());
                } catch (Exception e) {
                    //throw new RuntimeException(e);
                    //throw  new ApiSignatureException("签名错误");
                    resolver.resolveException((HttpServletRequest) requestWrapper, (HttpServletResponse) servletResponse, null, new ApiSignatureException("签名错误！"));
                    return;

                }
                //  解密数据操作  AKS_PARAMETER
                String decryptData = AESUtil.decryptFromBase64(encryptData, aesKey);
                log.info("Get请求:{}，解密之后参数：{}", requestURI, decryptData);
                //将GET请求中的Parameter参数赋值入RequestWrapper中封装的其它参数存放集合Map中
                com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();

                Map<String, Object> map = new HashMap<>();
                if (!StringUtils.isBlank(decryptData)) {
                    map = objectMapper.readValue(decryptData, Map.class);
                    Set set = map.keySet();
                    for (Iterator iterator = set.iterator(); iterator.hasNext(); ) {
                        Object obj = (Object) iterator.next();
                        Object value = (Object) map.get(obj);
                        if (value == null || value.equals("") || value.equals("null") || obj.toString().length() == 0) {
                            iterator.remove();
                        }
                    }
                }
                //将aseKey数据也存入map额外参数中
                map.put("aesKey", aesKey);
                requestWrapper.setParameterMap(map);
            }
        }
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    /**
     * Description:  判断当前请求是否需要加密解密数据
     *
     * @param request
     * @return boolean
     */
    private boolean needAks(HttpServletRequest request) {
        String enableAKS = request.getHeader(AKS_ENABLE);
        return (enableAKS != null && enableAKS.equalsIgnoreCase("true")) ? true : false;
    }
}